package org.kernely.scheduling;

import java.text.ParseException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

import org.kernely.scheduling.service.KernelySchedulingService;
import org.quartz.CronTrigger;
import org.quartz.Job;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SchedulerFactory;
import org.quartz.Trigger;
import org.quartz.impl.StdSchedulerFactory;

/**
 * This class manages the scheduling of the Kernely Quartz jobs.
 * This is a Singleton in order to ensure that there is only one instance of 
 * the Scheduler.
 */
public class KernelyQuartzScheduler  implements KernelySchedulingService{
	private static Scheduler scheduler;
	private Map<Class <? extends Job>,JobDetail> registeredJobs;

	/**
	 * Constructor of the scheduler
	 * @throws SchedulerException
	 */
	public KernelyQuartzScheduler() throws SchedulerException {

		SchedulerFactory schedulerFactory = new StdSchedulerFactory();
		scheduler = schedulerFactory.getScheduler();
		registeredJobs = new HashMap<Class <? extends Job>,JobDetail>();

		// Starts the Scheduler
		try {
			KernelyQuartzScheduler.scheduler.start();
		} 
		catch (SchedulerException e) {
			e.printStackTrace();
		}
	}

	/**
	 * Gets a reference to the Scheduler.
	 * @return scheduler A reference to the Scheduler.
	 */
	public static Scheduler getScheduler() {
		return scheduler;
	}

	/**
	 * @throws ParseException 
	 * @see KernelySchedulingService#scheduleJob(Class, String, String)
	 */
	public void scheduleJob(Class <? extends Job> jobClass, String description, String cronExpression) throws ParseException {
		scheduleJob(jobClass,description,cronExpression,null,null);
	}

	/**
	 * @see KernelySchedulingService#scheduleJob(Class, String, String, Date, Date)
	 */
	public void scheduleJob(Class<? extends Job> jobClass, String description, String cronExpression, Date startTime, Date endTime) throws ParseException {
		// Create the job
		JobDetail jobDetail = createJobDetail(jobClass,description);

		// Register the job
		this.registeredJobs.put(jobClass,jobDetail);

		// Create the associated trigger
		CronTrigger trigger = new CronTrigger();
		trigger.setName(UUID.randomUUID().toString());
		trigger.setCronExpression(cronExpression);
		trigger.setJobGroup(Scheduler.DEFAULT_GROUP);
		trigger.setJobName(jobClass.getName());

		if (startTime != null){
			trigger.setStartTime(startTime);
		}
		if (endTime != null){
			trigger.setEndTime(endTime);
		}

		// Schedule the job
		this.schedule(jobDetail, trigger);
	}

	/**
	 * @see KernelyQuartzScheduler#registerJob(Class, String)
	 */
	public void registerJob(Class<? extends Job> jobClass, String description) {
		this.registeredJobs.put(jobClass,createJobDetail(jobClass,description));
	}


	private JobDetail createJobDetail(Class<? extends Job> jobClass, String description) {
		JobDetail jobDetail = new JobDetail(jobClass.getName(), Scheduler.DEFAULT_GROUP, jobClass);
		jobDetail.setDescription(description);
		return jobDetail;
	}

	private void schedule(JobDetail jobDetail, Trigger trigger){
		try {
			JobDetail existing = scheduler.getJobDetail(jobDetail.getName(),jobDetail.getGroup());

			if (existing == null){
				scheduler.scheduleJob(jobDetail, trigger);
			}
			else {
				// If the job already exists, we just schedule a new trigger
				scheduler.scheduleJob(trigger);
			}
		} catch (SchedulerException e) {
			e.printStackTrace();
		}
	}

	/**
	 * @see KernelySchedulingService#getRegisteredJobs()
	 */
	public Map<Class < ? extends Job >,JobDetail> getRegisteredJobs() {
		return registeredJobs;
	}

	/**
	 * @see KernelySchedulingService#scheduleRegisteredJob(Class, String)
	 */
	public void scheduleRegisteredJob(Class<? extends Job> jobClass, String cronExpression) throws ParseException {
		JobDetail job = registeredJobs.get(jobClass);
		this.scheduleJob(jobClass, job.getDescription(), cronExpression, null, null);
	}

	/**
	 * @see KernelySchedulingService#scheduleRegisteredJob(Class, String, Date, Date)
	 */
	public void scheduleRegisteredJob(Class<? extends Job> jobClass, String cronExpression, Date startTime, Date endTime) throws ParseException {
		JobDetail job = registeredJobs.get(jobClass);
		this.scheduleJob(jobClass, job.getDescription(), cronExpression, startTime,endTime);
	}
}